/**
 * 非阻塞的服务端：socket服务端，接收客户端发来的请求，统计请求次数，把接收到的
 * 信息逆序然后发给对应的客户端。
 *
 * 2008-3-19
 * @author 李威
 *
 *
 */
package lsd.nio.server;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Set;
import java.util.Timer;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import lsd.util.ReadCount;
import lsd.util.SocketUtil;

public class MainServerService {
    private static int      receiveCount = 0;
    private Charset         charset      = Charset.forName("GBK");
    private ExecutorService pool         = null;

    /**
     * 把字符串逆序输出
     * 
     * @param s 需要转换的字符串
     * @return 逆序的字符串
     */
    public String inverseString(String s) {
        StringBuffer sb = new StringBuffer();
        char[] c = s.toCharArray();
        for (int i = s.length() - 1; i > -1; i--) {
            sb.append(c[i]);
        }
        return sb.toString();
    }

    /**
     * 服务端初始化发放
     * @param ports 存放服务端监听端口数组
     */
    public void init(int[] ports) throws IOException {
        new Timer().schedule(new ReadCount(), 0, 1000 * 10);
        pool = Executors.newFixedThreadPool(100);
        //创建一个选择器，用来注册事件
        Selector selector = Selector.open();

        //为每一个端口建立一个监听
        for (int i = 0; i < ports.length; i++) {
            //创建一个服务端的通道 
            ServerSocketChannel ssc = ServerSocketChannel.open();

            //设置为阻塞模式
            ssc.configureBlocking(false);

            //绑定服务端监听的端口
            //-服务器套接字通道不是侦听网络套接字的完整抽象。
            //-必须通过调用 socket 方法所获得的关联 ServerSocket 对象来完成对套接字选项的绑定和操作。
            ServerSocket ss = ssc.socket();
            ss.setReuseAddress(true);
            InetSocketAddress address = new InetSocketAddress(ports[i]);
            ss.bind(address);

            //为ServerSocketChannel在Selector中注册
            ssc.register(selector, SelectionKey.OP_ACCEPT);
        }
        System.out.println("server start");

        //循环监听事件，并处理激发的事件
        while (true) {
            //这个方法会阻塞，直到有事件到来将之激活，这时返回事件的个数
            selector.select();
            //			System.out.println("A new connect");
            Set<SelectionKey> keys = selector.selectedKeys();
            Iterator<SelectionKey> iter = keys.iterator();
            while (iter.hasNext()) {
                //取得绑定的契约
                SelectionKey key = iter.next();

                iter.remove();
                //判断契约对应的事件，根据不同的事件进行不同的处理
                if (key.isAcceptable()) {
                    //连接请求适合ServerSocketChannel
                    ServerSocketChannel ssc = (ServerSocketChannel) key.channel();

                    //获得请求对应的socket通道
                    SocketChannel sc = ssc.accept();

                    //设置socket通道为非阻塞的，不设置默认为阻塞的
                    sc.configureBlocking(false);
                    ByteBuffer buffer = ByteBuffer.allocate(10240);
                    //注册读写监听，并把一个buffer作为附件放到契约key中

                    sc.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE, buffer);
                }
                if (key.isReadable()) {
                    //读
                    try {
                        if (receive(key) == true)
                            continue;
                    } catch (Exception e) {
                        //e.printStackTrace();
                        if (key != null) {
                            key.cancel();
                            key.channel().close();
                            continue;
                        }
                    }
                }
                if (key.isWritable()) {
                    //写
                    if (send(key) == true)
                        continue;
                }

            }
        }

    }

    public static void main(String[] args) throws IOException {
        int[] a = { SocketUtil.port };
        try {
            new MainServerService().init(a);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 接受客户端的请求，存储到key的附件内
     * @param key 当前时间的契约
     * @return 是否断开当前通道，true则断开，false则保持连通
     * @throws IOException
     */
    public boolean receive(SelectionKey key) throws IOException {
        //		System.out.println("read");
        ByteBuffer buffer = (ByteBuffer) key.attachment();
        SocketChannel sc = (SocketChannel) key.channel();
        ByteBuffer readbb = ByteBuffer.allocate(64);
        buffer.limit(buffer.capacity());
        //当容量小于50%时暂停读入
        if (buffer.remaining() < buffer.capacity() * 0.5)
            return false;
        //读取数据到读缓存
        int sum = sc.read(readbb);
        //把读入的数据存入附件buffer中
        readbb.flip();
        //只存储readbb中有数据的部分到buffer,例如readbb中只读入了32字节数据，那么只把这32字节的数据存入buffer
        buffer.limit(buffer.capacity());
        buffer.put(readbb);
        return false;
    }

    /**
     * 发送key附件中的信息到客户端
     * @param key
     * @throws IOException
     */
    public boolean send(SelectionKey key) throws IOException {
        ByteBuffer buffer = (ByteBuffer) key.attachment();
        SocketChannel sc = (SocketChannel) key.channel();

        //将buffer转换为字符串bufferString
        String bufferString = "";
        StringBuffer sb = new StringBuffer();
        do {
            buffer.flip();
            bufferString = decode(buffer);
            int end = bufferString.indexOf(SocketUtil.endTag);
            if (end == -1)
                return false;//当前没有找到输出终结符,直接返回
            //读出一段要输出的数据，放入输出缓存中
            String outputString = bufferString.substring(0, end + SocketUtil.endTagLength);
            System.out.println(outputString);
            int locate = outputString.indexOf("bye" + SocketUtil.endTag);
            if (locate < 0) {//如果没到bye中介符，逆序输出数据
                //逆序输出 begin
                sb.delete(0, sb.length());
                sb.append(outputString.substring(0, outputString.indexOf(":") + 1));
                String temp = outputString.substring(outputString.indexOf(":") + 1,
                    outputString.indexOf(SocketUtil.endTag));
                sb.append(inverseString(temp));
                sb.append(SocketUtil.endTag);
                //			System.out.println(sb.toString());
                outputString = sb.toString();
                //逆序输出 begin end
            }
            ByteBuffer write = encode(outputString);
            while (write.hasRemaining())
                //把write中的信息输出完，非阻塞时一次输出的量是不确定的
                sc.write(write);
            receiveCount++;

            //从buffer中删除已经输出的部分
            buffer.position(write.limit());
            buffer.compact();
            //判断是否输出到bye信号，输出到bye信号为输出完毕。关闭资源。

            if (locate > 0) {
                key.cancel();
                sc.close();
                System.out.println("关闭" + outputString.charAt(locate - 1) + "与服务器的连接");
                return true;
            }
            //			System.out.println("serverSend:"+buffer.position()+":"+buffer.limit()+":"+buffer.capacity()+":"+buffer.remaining());//测试
        } while (true);
    }

    /**
     * 解码：把ByteBuffer转换成String
     * @param bb 需要转换的ByteBuffer
     * @return 转换后的String
     */
    public String decode(ByteBuffer bb) {
        //		bb.flip();
        CharBuffer charBuffer = charset.decode(bb);
        return charBuffer.toString();
    }

    /**
     * 编码：把String转换成ByteBuffer
     * @param s 需要转换的字符床
     * @return 转换后的ByteBuffer
     */
    public ByteBuffer encode(String s) {
        return charset.encode(s);
    }

    public static int getCount() {
        return receiveCount;
    }
}
